/*
 * Copyright 2011-2012 by The Regents of the University of California Licensed
 * under the Apache License, Version 2.0 (the "License"); you may not use this
 * file except in compliance with the License. you may obtain a copy of the
 * License from
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package edu.ucsd.db.bassarisk.schema;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

import edu.ucsd.db.bassarisk.exception.SchemaValidationException;
import edu.ucsd.db.bassarisk.query.Constants;

public class RunClass extends DataSequenceClass {
  public static final RunClass INDEX_ROOT = new RunClass(Constants.IndexRootRunName);
  private static final List<SchemaNode> positionSpaceNodes = new ArrayList<SchemaNode>();
  private static final Logger LOG = Logger.getLogger(RunClass.class.getName());
  private final Set<SchemaRunNode> subClasses;

  public RunClass(final String name) {
    super(name);
    this.subClasses = new HashSet<SchemaRunNode>();
    this.isInitialized = false;
    this.isRefined = false;
  }

  public RunClass(final String name, final Set<SchemaRunNode> refinement) {
    super(name);
    this.subClasses = new HashSet<SchemaRunNode>();
    this.subClasses.addAll(refinement);
    this.isInitialized = true;
    this.isRefined = true;
    this.fullClassSize = -1;
    this.refinedClassSize = this.subClasses.size();
  }

  @Override
  public Collection<? extends SchemaNode> getRefinement() {
    if (this.isPositionSpace()) {
      if (positionSpaceNodes.size() == 0) {
        positionSpaceNodes.add(SchemaNode.POSITON_SPACE);
      }
      return positionSpaceNodes;
    } else {
      if (!this.isInitialized) {
        String msg =
            "Refinement not avalible until sequence class has been "
                + "initialized by a shcmea.";
        throw new UnsupportedOperationException(msg);
      }
      return this.subClasses;
    }
  }

  @Override
  public int initailize(final Schema schema) throws SchemaValidationException {
    this.subClasses.clear();
    this.subClasses.addAll(schema.collectRunNodesForName(this.name));
    this.isRefined = false;
    this.isInitialized = true;
    this.fullClassSize = this.subClasses.size();
    this.refinedClassSize = this.subClasses.size();
    if (this.refinedClassSize == 0 && !this.isPositionSpace()) {
      String msg = String.format("Unknown run name %s.", this.name);
      LOG.severe(msg);
      throw new SchemaValidationException(msg);
    }
    return this.subClasses.size();
  }

  @Override
  public boolean isPositionSpace() {
    return this.name.equals(Constants.PositionSpaceFlag);
  }

  @Override
  public DataSequenceClass
      makeUnion(final List<DataSequenceClass> unionedSequenceClasses) {
    Set<SchemaRunNode> unionRefinement = new HashSet<SchemaRunNode>();
    for (DataSequenceClass dsc : unionedSequenceClasses) {
      // Combination bindings are only valid over bindings with the same
      // run-class
      assert dsc.getName().equals(this.name);
      assert dsc instanceof RunClass;
      unionRefinement.addAll(((RunClass) dsc).subClasses);
    }
    return new RunClass(this.name, unionRefinement);
  }

  @Override
  protected int refinementSize() {
    return this.subClasses.size();
  }

  @Override
  public String toString() {
    if (this.isPositionSpace()) {
      return "RunsClass(Position Space)";
    } else {
      if (this.isInitialized) {
        return String.format("RunsClass(%s, %s of %s)", this.name,
                             this.refinedClassSize, this.fullClassSize);
      } else {
        return String.format("RunsClass(%s, uninitialized)", this.name,
                             this.refinedClassSize, this.fullClassSize);
      }
    }
  }
}
